/**
 * 函数指针fn，的确是一个好注意
 * 注意是fn，不是Fn(特质)
 * .函数指针实现了所有三个闭包 trait（Fn、FnMut 和 FnOnce），所以总是可以在调用期望闭包的函数时传递函数指针作为参数
 * ---
 * 利用函数指针可以构建相对灵活的系统，起到的作用类似特质
 */
fn add_one(x: i32) -> i32 {
    x + 1
}
fn multiply_10(x: i32) -> i32 {
    x * 10
}

type  FnPointer = fn(i32,i32) -> String;

fn  link(f: FnPointer, x:i32,y:i32) {
   let dst= f(x,y);
   println!("{}",dst);
}

/**
 * 第一个参数是函数指针，fn指针实现了FnOnce,FnMut,Fn特质，所以可以接受匿名函数作为参数。
 */
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}


fn 书本例子() {
    let list_of_numbers = vec![1, 2, 3];
    //利用rustc，象我这样的傻瓜也能写rust!
    let list_of_strings: Vec<String> =
        list_of_numbers.iter().map(|i| "0".to_owned()+&i.to_string()).collect();
    println!("{:?}", list_of_strings);
    let list_of_strings2: Vec<String> =
        list_of_numbers.iter().map(ToString::to_string).collect();
    println!("{:?}", list_of_strings2);
}


fn main() {
    //封装再封装，就可以解决一些复杂问题，至于是否高效是另外一回事了.
    let funcs=[add_one, multiply_10];
    let numbers = [123, 233, 3232, 32334, 5333];
    for i in 0..numbers.len() {
        let answer1 = do_twice(funcs[0], numbers[i]);
        let answer2=  do_twice(funcs[1], numbers[i]);
        println!("({}+1)*2= {},({}*10)*2={}",numbers[i],answer1,numbers[i],answer2);    
    }

    // do_twice同时也能接受匿名函数,rust的解释：fn指针实现了FnOnce,FnMut,Fn特质，所以可以接受匿名函数作为参数。
    
    println!("\n匿名函数也能被接受作为参数的演示....");
    let answer3 = do_twice(|x| x * 2+99, numbers[0]);
    println!("{}经过处理后等于{}",numbers[0],answer3);

    // 书本例子，让我们感兴趣是类似java等语言的流式操作
    书本例子();

    //类型别名
    let link_to = |x:i32,y:i32| x.to_string()+&y.to_string();
    link(link_to, 123,899);

    //FnOnce特质，只能被调用一次
    batman(|x:i32,y:i32| x.to_string()+&y.to_string()+" 黑蝙蝠侠 🦇");
    eggman(|x:i32,y:i32| x.to_string()+&y.to_string()+" 蛋蛋大侠 🥚🥚");
    pigman(&|x:i32,y:i32| x.to_string()+&y.to_string()+" 猪猪侠士 🐷");
    pigman(&linktow);
}

/**
 * 函数指针的替代者1  -- 利用特质绑定+通用参数
 */ 
fn batman<T: FnOnce(i32,i32)->String>(f:T) {
    let result=f(1094,101);
    println!("{}",result);
}
/**
 * 函数指针的替代者2 -- 特质绑定的简化写法，rust 1.13引入的trait bound syntax
 */
fn eggman(f:impl FnOnce(i32,i32)->String) {
    let result=f(1094,103);
    println!("{}",result);
}
/**
 * 函数指针的替代者3 -- 使用特质对象
 * 这里的定义比较特殊，用上了&dyn,为什么要这样了？
 * 因为特质对象必须使用动态分发，这是rust规定的
 * 要使用&,是因为特质对象大小不确定，rustc不知道大小,所以只能加一个引用，以便构成一个指针（编译器可以知道大小）
 */
fn pigman(f:&dyn Fn(i32,i32)->String) {
    let result=f(2094,103);
    println!("{}",result);
}
fn linktow(x:i32,y:i32) -> String {
    x.to_string()+&y.to_string()+" 猪猪侠士 🐷"
}